using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using FreeBase.Fluent.MQLConverter;
using FreeBase.Fluent;
using System.Reflection;

using log4net;

using FreeBase.Domain;

namespace FreeBase.Linq
{
	/// <summary>
	/// Description of FreebaseQueryProvider.
	/// </summary>
	public class FreebaseQueryProvider : QueryProvider
	{
		private readonly ILog logger = LogManager.GetLogger(typeof(FreebaseExpressionVisitor));
		
		IMQLConverter _parser = new FluentMQLConverter();
		IFreebaseClient _client = null;
		IResponseParser _responseParser = new ResponseParser();
		
		public FreebaseQueryProvider( IFreebaseClient client )
		{
			this._client = client;
		}
		
		public FreebaseQueryProvider( IFreebaseClient client, IResponseParser responseParser )
			: this( client )
		{
			this._responseParser = responseParser;
		}
		
		public override String GetQueryText(Expression expression)
		{
			FBQuery criteria;

			// Retrieve criteria
			criteria = new FreebaseExpressionVisitor().ProcessExpression( expression );

			// Generate Mql
			String mql = _parser.FluentToMQL( criteria );
			logger.Debug( "MQL generated is : " + mql );
			return mql;
		}
		
		public override TResult Execute2<TResult>(Expression expression)
		{
			if( expression == null )
				throw new ArgumentNullException( "expression" );
			
			logger.Debug( "T is of type " + typeof(TResult).Name );
			
			//get mql query (a string) from the Linq expression
			String mql = GetQueryText( expression );
			
			//now send the query and await a response
			string response = string.Empty;
			try{
				response = _client.Read( mql );
			}
			catch( Exception e ){
				throw new ApplicationException( "Could not perform the web query", e );
			}

			if( string.IsNullOrEmpty( response ) )
				throw new ApplicationException( "Your query returned no response" );
			
			ResponseForLinq<TResult> parsedResponse = this._responseParser.ParseForLinq<TResult>( response );
			
			if( parsedResponse == null )
			{
				logger.Error( "ParsedResponse returned null results" );
				throw new ApplicationException( "ParsedResponse returned null results" );
			}
			
			TResult parsedResults = parsedResponse.Result;
			if( parsedResults == null )
			{
				logger.Error( "ParsedResults returned null results" );
				throw new ApplicationException( "ParsedResults returned null results" );
			}
			
			return parsedResults;
		}
	}
}
